home *** CD-ROM | disk | FTP | other *** search
- ; DDIR.ASM -- Double Column Sorted DIR Command
- ; ========
- ; (C) Copyright Charles Petzold, 1985
- ;
- ; COM file format
- ;
-
- CSEG Segment
-
- Assume CS:CSEG, DS:CSEG
-
- Org 002Ch ; Offset of Environment
- Environment Label Byte
-
- Org 007Bh ; Parameter for COMMAND.COM
- NewParameter Label Byte
-
- Org 0080h ; Parameter passed to program
- OldParameter Label Byte
-
- Org 0100h ; Entry point
- Entry: Jmp Begin
-
- ; All Data
- ; --------
-
- db '(C) Copyright Charles Petzold, 1985'
-
- DosVersMsg db "Needs DOS 2.0 +$" ; Error messages
- MemAllocMsg db "Memory Problem$"
- CommandMsg db "COMMAND Problem$"
-
- Comspec db "COMSPEC=" ; Search string in environment
- CommandAsciiz dd ? ; Eventual pointer to COMMAND
-
- ParamBlock dw ? ; Parameter Block for EXEC
- dw NewParameter,? ; First ? must be replaced
- dw 5Ch,? ; with Environment segment;
- dw 6Ch,? ; others with this segment
-
- OldInterrupt21 dd ? ; For vector address storage
-
- BufferPtr dw Offset FileBuffer ; For storing files listing
- CharCounter dw 0 ; Keeps track of characters
- NowDoingFile db 0 ; Flagged for file printed
- WithinFileList db 0 ; Flagged for file list
- FileCounter dw 0 ; Keeps track of files
- LineCounter db 0 ; For pausing at screen end
-
- PauseMessage db 6 dup (205)," Press any key to continue "
- db 6 dup (205),181
- PauseMsgEnd Label Byte
-
- ; Check DOS Version
- ; -----------------
-
- Begin: Mov AH,30h ; DOS Version function call
- Int 21h ; Call DOS
- Cmp AL,2 ; Check if version 2
- Jae DosVersOK ; If equal or over, all OK
-
- Mov DX,Offset DosVersMsg ; Wrong DOS version message
- ErrorExit: Mov AH,9 ; Set up for string write
- Int 21h ; Call DOS for message
-
- Int 20h ; Dishonorable discharge
-
- ; Adjust stack and un-allocate rest of memory
- ; -------------------------------------------
-
- DosVersOK: Mov DI,Offset FileBuffer ; Place to save files
- Mov CX,528 * 39 ; Allow room for 528 files
- Mov AL,' ' ; Will clear with blanks
- Cld ; Forward direction
- Rep Stosb ; Clear the area
-
- Mov BX,(Offset FileBuffer) + (528 * 39) + 100h
- ; New end of program
- Mov SP,BX ; Set the stack pointer
- Add BX,15 ; Add 15 for rounding
- Mov CL,4 ; Number of shifts
- Shr BX,CL ; Convert AX to segment
-
- Mov AH,4Ah ; DOS call to shrink down
- Int 21h ; allocated memory
-
- Mov DX,Offset MemAllocMsg ; Possible error message
- Jc ErrorExit ; Only print it if Carry set
-
- ; Search for Comspec in Environment
- ; ---------------------------------
-
- Mov ES,[Environment] ; Environment Segment
- Sub DI,DI ; Start search at beginning
- Cld ; String increment to forward
-
- TryThis: Cmp Byte Ptr ES:[DI],0 ; See if end of environment
- Jz NoFindComSpec ; If so, we have failed
-
- Push DI ; Save environment pointer
- Mov SI,Offset ComSpec ; String to search for
- Mov CX,8 ; Characters in search string
- Repz Cmpsb ; Check if strings are same
- Pop DI ; Get back the pointer
-
- Jz FoundComspec ; Found string only zero flag
-
- Sub AL,AL ; Zero out AL
- Mov CX,8000h ; Set for big search
- Repnz Scasb ; Find the next zero in string
- Jmp TryThis ; And do the search from there
-
- NoFindComSpec: Mov DX,Offset CommandMsg ; Message for COMSPEC error
- Jmp ErrorExit ; Print it and exit
-
- FoundComspec: Add DI,8 ; So points after 'COMSPEC='
- Mov Word Ptr [CommandASCIIZ],DI ; Save the address of
- Mov Word Ptr [CommandASCIIZ + 2],ES ; COMMAND ASCIIZ
-
- ; Set up parameter block for EXEC call
- ; ------------------------------------
-
- Mov [ParamBlock],ES ; Segment of Environment string
- Mov [ParamBlock + 4],CS ; Segment of this program
- Mov [ParamBlock + 8],CS ; so points to FCB's
- Mov [ParamBlock + 12],CS ; and NewParameter
-
- ; Save and set Interrupt 21h vector address
- ; -----------------------------------------
-
- Mov AX,3521h ; DOS call to get Interrupt 21
- Int 21h ; vector address
- Mov Word Ptr [OldInterrupt21],BX ; Save offset
- Mov Word Ptr [OldInterrupt21 + 2],ES ; And segment
-
- Mov DX,Offset NewInterrupt21; Address of new Interrupt 21
- Mov AX,2521h ; Do DOS call to
- Int 21h ; set the new address
-
- ; Fix up new parameter for "/C DIR" String
- ; ------------------------------------
-
- Mov AL,[OldParameter] ; Number of parameter chars
- Add AL,5 ; We'll be adding five more
- Mov [NewParameter],AL ; Save it
- Mov Word Ptr [NewParameter + 1],'C/' ; i.e. "/C"
- Mov Word Ptr [NewParameter + 3],'ID' ; Then "DI"
- Mov Byte Ptr [NewParameter + 5],'R' ; And "R"
-
- ; Load COMMAND.COM
- ; -----------------
-
- Push CS ; Push this segment so we can
- Pop ES ; set ES to it
- Mov BX,Offset ParamBlock ; ES:BX = address of block
- Lds DX,[CommandAsciiz] ; DS:DX = address of ASCIIZ
- Mov AX,4B00h ; EXEC call 4Bh, type 0
- Int 21h ; Load command processor
-
- ; Return from COMMAND.COM
- ; -----------------------
-
- Mov AX,CS ; Get this segment in AX
- Mov DS,AX ; Set DS to it
- Mov SS,AX ; And SS for stack segment
- Mov SP,(Offset FileBuffer) + (528 * 39) + 100h
- ; Set Stack again
-
- PushF ; Save Carry for error check
- Push DS ; Save DS during next call
-
- Mov DX,Word Ptr [OldInterrupt21] ; Old Int 21 offset
- Mov DS,Word Ptr [OldInterrupt21 + 2]; and segment
- Mov AX,2521h ; Call DOS to set vector
- Int 21h ; address to original
-
- Pop DS ; Restore DS to this segment
- PopF ; Get back Carry flage
-
- Jnc NormalEnd ; Continue if no error
-
- Mov DX,Offset CommandMsg ; Otherwise we'll print error
- Jmp ErrorExit ; message and exit
-
- NormalEnd: Int 20h ; Terminate program
-
- ; New Interrupt 21h
- ; -----------------
-
- NewInterrupt21 Proc Far
-
- Sti ; Allow further interrupts
- Cmp AH,40h ; Check if file / device write
- Je CheckHandle ; If so, continue checks
-
- SkipIntercept: Jmp CS:[OldInterrupt21] ; Just jump to old interrupt
-
- CheckHandle: Cmp BX,1 ; Check if standard output
- Jne SkipIntercept ; Not interested if not
-
- PushF ; Push all registers that
- Push AX ; we'll be messing with
- Push CX
- Push SI
- Push DI
- Push ES
-
- Push CS ; Push the code segment
- Pop ES ; So we can set ES to it
- Cld ; Forward for string transfers
- Mov SI,DX ; Now DS:SI = text source
- Mov DI,CS:[BufferPtr] ; And ES:DI = text destination
-
- Cmp CX,2 ; See if two chars to write
- Jne RegularChars ; If not, can't be CR/LF
-
- Cmp Word Ptr DS:[SI],0A0Dh ; See if CR/LF being written
- Jne RegularChars ; Skip rest if not CR/LF
-
- Mov CX,CS:[CharCounter] ; Get characters in line
- Mov CS:[CharCounter],0 ; Start at new line
- Cmp CS:[NowDoingFile],1 ; See if CR/LF terminates file
- Jnz AllowTransfer ; If not, just write to screen
-
- Mov AX,39 ; Max characters per line
- Sub AX,CX ; Subtract those passed
- Add CS:[BufferPtr],AX ; Kick up pointer by that
- Mov CS:[NowDoingFile],0 ; Finished with file
- Jmp PopAndReturn ; So just return to COMMAND
-
- RegularChars: Add CS:[CharCounter],CX ; Kick up counter by number
- Cmp CS:[CharCounter],CX ; See if beginning of line
- Jne NotLineBegin ; If not, must be in middle
-
- Cmp Byte Ptr DS:[SI],' ' ; See if first char is blank
- Jne ItsAFile ; If not, it's a file line
-
- Cmp CS:[WithinFileList],1 ; See if doing file listing
- Jne AllowTransfer ; If not, just print stuff
-
- Call SortAndList ; Files done -- sort and list
- Mov CS:[WithinFileList],0 ; Not doing files now
- Jmp Short AllowTransfer ; So just print the stuff
-
- ItsAFile: Cmp CS:[FileCounter],528 ; See if 11 buffer filled up
- Jb NotTooManyFiles ; If not just continue
-
- Push CX ; Otherwise, save this register
- Call SortAndList ; Print all up to now
- Mov CS:[FileCounter],0 ; Reset the counter
- Mov DI,Offset FileBuffer ; And the pointer
- Mov CS:[BufferPtr],DI ; Save the pointer
- Mov CX,528 * 39 ; Will clear for 528 files
- Mov AL,' ' ; With a blank
- Rep Stosb ; Clear it out
- Pop CX ; And get back register
-
- NotTooManyFiles:Mov CS:[WithinFileList],1 ; We're doing files now
- Mov CS:[NowDoingFile],1 ; And a file in particular
- Inc CS:[FileCounter] ; So kick up this counter
-
- NotLineBegin: Cmp CS:[NowDoingFile],1 ; See if doing files
- Je StoreCharacters ; If so, store the stuff
-
- AllowTransfer: Pop ES ; Pop all the registers
- Pop DI
- Pop SI
- Pop CX
- Pop AX
- PopF
-
- Jmp SkipIntercept ; And go to DOS for print
-
- StoreCharacters:Mov DI,CS:[BufferPtr] ; Set destination
- Rep Movsb ; Move characters to buffer
- Mov CS:[BufferPtr],DI ; And save new pointer
-
- PopAndReturn: Pop ES ; Pop all the registers
- Pop DI
- Pop SI
- Pop CX
- Pop AX
- PopF
-
- Mov AX,CX ; Set for COMMAND.COM
- Clc ; No error here
- Ret 2 ; Return with CY flag cleared
-
- NewInterrupt21 EndP
-
- ; Sort Files
- ; ----------
-
- SortAndList: Push BX ; Push a bunch of registers
- Push DX
- Push SI
- Push DS
-
- Push CS ; Push CS
- Pop DS ; so we can set DS to it
- Assume DS:CSEG ; And inform the assembler
-
- Mov DI,Offset FileBuffer ; This is the beginning
- Mov CX,[FileCounter] ; Number of files to sort
- Dec CX ; Loop needs one less than that
- Jcxz AllSorted ; But zero means only one file
-
- SortLoop1: Push CX ; Save the file counter
- Mov SI,DI ; Set source to destination
-
- SortLoop2: Add SI,39 ; Set source to next file
-
- Push CX ; Save the counter,
- Push SI ; compare source,
- Push DI ; and compare destination
-
- Mov CX,39 ; 39 characters to compare
- Repz Cmpsb ; Do the compare
- Jae NoSwitch ; Jump if already in order
-
- Pop DI ; Get back these registers
- Pop SI
-
- Push SI ; And push them again for move
- Push DI
-
- Mov CX,39 ; 39 characters
- SwitchLoop: Mov AL,ES:[DI] ; Character from destination
- Movsb ; Source to destination
- Mov DS:[SI - 1],AL ; Character to source
- Loop SwitchLoop ; For the rest of the line
-
- NoSwitch: Pop DI ; Get back the registers
- Pop SI
- Pop CX
- Loop SortLoop2 ; And loop for next file
-
- Pop CX ; Get back file counter
- Add DI,39 ; Compare with next file
- Loop SortLoop1 ; And loop again
-
- ; Now Display Sorted Files
- ; ------------------------
-
- AllSorted: Mov SI,Offset FileBuffer ; This is the beginning
- Mov CX,[FileCounter] ; Number of files to list
- Inc CX ; In case CX is odd
- Shr CX,1 ; CX now is number of lines
-
- SetIncrement: Mov BX,24 * 39 ; Increment for double list
- Cmp CX,24 ; But use it only if a full
- Jae LineLoop ; screen is printed
-
- Mov AX,39 ; Otherwise find increment
- Mul CX ; by multiplying CX by 39
- Mov BX,AX ; And make that the increment
-
- LineLoop: Call PrintFile ; Print the first column file
- Mov AL,' ' ; Skip one space
- Call PrintChar ; by printing blank
- Mov AL,179 ; Put a line down the middle
- Call PrintChar
- Mov AL,' ' ; Skip another space
- Call PrintChar
-
- Add SI,BX ; Bump up source by increment
- Sub SI,39 ; But kick down by 39
-
- Call PrintFile ; Print the second column file
- Call CRLF ; And terminate line
-
- Sub SI,BX ; Bring pointer back down
-
- Inc [LineCounter] ; One more line completed
- Cmp [LineCounter],24 ; Have we done whole screen?
- Jz PauseAtEnd ; If so, gotta pause now
-
- Loop LineLoop ; Otherwise just loop
- Jmp Short AllFinished ; And jump out when done
-
- PauseAtEnd: Mov [LineCounter],0 ; Reset the counter
- Add SI,BX ; Go to next file
-
- Push BX ; Save these registers
- Push CX
- Mov DX,Offset PauseMessage ; Test to print
- Mov CX,Offset PauseMsgEnd - Offset PauseMessage
- ; Number of characters
- Mov BX,2 ; Standard ERROR Output
- Mov AH,40h ; Display to screen
- Int 21h ; By calling DOS
- Pop CX ; Retrieve pushed registers
- Pop BX
-
- Mov AH,8 ; Wait for character
- Int 21h ; Through DOS call
-
- Call CRLF ; Go to next line
-
- Loop SetIncrement ; And recalculate increment
-
- AllFinished: Pop DS ; Done with subroutine
- Pop SI
- Pop DX
- Pop BX
- Ret ; So return to caller
-
- ; Display Routines
- ; ----------------
-
- PrintChar: Mov DL,AL ; Print character in AL
- Mov AH,2 ; By simple DOS call
- Int 21h
- Ret ; And return
-
- CRLF: Mov AL,13 ; Print a carriage return
- Call PrintChar
- Mov AL,10 ; And a line feed
- Call PrintChar
- Ret ; And return
-
- PrintString: Lodsb ; Get character from SI
- Call PrintChar ; Print it
- Loop PrintString ; Do that CX times
- Ret ; And return
-
- PrintFile: Push CX ; Save the counter
- Mov CX,32 ; Bytes for Name, Size, & Date
- Call PrintString ; Print it
- Inc SI ; Skip one space before time
- Mov CX,6 ; Bytes for Time
- Call PrintString ; It's a print!
- Pop CX
- Ret ; And return
-
- FileBuffer Label Byte ; Points to end of code
-
- CSEG EndS ; End of segment
-
- End Entry ; Denotes entry point